function convert(input) { alert('Error. Contact us if this keeps happening.'); document.getElementById('modal-contact').classList.add('show'); } window.ctMaxUploadFiles = 1; var _efState = { blob: null, fileName: 'video', duration: 0 }; function _efParseTimestamp(s) { if (!s) return 0; s = s.trim(); if (/^\d+(\.\d+)?$/.test(s)) return parseFloat(s); var m = s.match(/^(?:(\d+):)?(\d+):(\d+(?:\.\d+)?)$/); if (m) { var h = parseInt(m[1] || '0', 10); var mm = parseInt(m[2], 10); var ss = parseFloat(m[3]); return h * 3600 + mm * 60 + ss; } var m2 = s.match(/^(\d+):(\d+(?:\.\d+)?)$/); if (m2) return parseInt(m2[1], 10) * 60 + parseFloat(m2[2]); return 0; } function processFile(blob, fileName) { _efState.blob = blob; _efState.fileName = fileName || 'video'; $('#file-drop-zone').addClass('collapsedDropZone'); (function(){ var $b=$('#file-drop-zone .upload-file-button'); var l=$b.data('change-label'); if(l) $b.text(l); })(); var ws = document.getElementById('ef-workspace'); if (ws) ws.style.display = 'block'; var video = document.getElementById('ef-video'); if (video) { video.src = URL.createObjectURL(blob); video.onloadedmetadata = function() { _efState.duration = video.duration || 0; }; } } function _efSeekTo(video, t) { return new Promise(function(resolve, reject) { var handled = false; function onSeeked() { if (handled) return; handled = true; video.removeEventListener('seeked', onSeeked); video.removeEventListener('error', onErr); requestAnimationFrame(function() { resolve(); }); } function onErr() { if (handled) return; handled = true; video.removeEventListener('seeked', onSeeked); video.removeEventListener('error', onErr); reject(new Error('Seek failed at ' + t.toFixed(2) + 's')); } video.addEventListener('seeked', onSeeked); video.addEventListener('error', onErr); try { video.currentTime = t; } catch (e) { onErr(); } }); } function _efCanvasBlob(canvas, format) { return new Promise(function(resolve, reject) { var mime = format === 'jpg' ? 'image/jpeg' : 'image/png'; var quality = format === 'jpg' ? 0.95 : undefined; canvas.toBlob(function(b) { if (!b) reject(new Error('toBlob failed')); else resolve(b); }, mime, quality); }); } function _efApply() { if (!_efState.blob) { alert('Drop a video first.'); return; } var mode = document.getElementById('ef-mode').value; var format = document.getElementById('ef-format').value; var status = document.getElementById('ef-status'); var btn = document.getElementById('ef-apply'); var video = document.getElementById('ef-video'); if (!video || !_efState.duration) { alert('Video metadata not loaded yet, please retry.'); return; } var times = []; if (mode === 'single') { var t = _efParseTimestamp(document.getElementById('ef-timestamp').value); if (t < 0 || t > _efState.duration) { alert('Timestamp is outside the video duration.'); return; } times.push(t); } else { var iv = parseFloat(document.getElementById('ef-interval').value); if (!isFinite(iv) || iv <= 0) { alert('Interval must be a positive number.'); return; } var maxFrames = 600; for (var t2 = 0; t2 < _efState.duration && times.length < maxFrames; t2 += iv) times.push(t2); if (times.length === maxFrames) { if (!confirm('That interval produces ' + times.length + '+ frames. Only the first ' + maxFrames + ' will be extracted. Continue?')) return; } } if (btn) btn.disabled = true; var w = video.videoWidth || 0; var h = video.videoHeight || 0; if (!w || !h) { alert('Video dimensions are unknown.'); if (btn) btn.disabled = false; return; } var canvas = document.createElement('canvas'); canvas.width = w; canvas.height = h; var ctx = canvas.getContext('2d'); var base = (_efState.fileName || 'video').replace(/\.[^.]+$/, ''); function pad(n, w) { var s = String(n); while (s.length < w) s = '0' + s; return s; } var doneSingle = times.length === 1; if (doneSingle) { _efSeekTo(video, times[0]).then(function() { ctx.drawImage(video, 0, 0, w, h); return _efCanvasBlob(canvas, format); }).then(function(blob) { var ts = times[0].toFixed(2).replace('.', '_'); add_file_output(URL.createObjectURL(blob), base + '-' + ts + 's.' + format); if (status) status.textContent = 'Done.'; if (btn) btn.disabled = false; }).catch(function(err) { if (status) status.textContent = ''; if (btn) btn.disabled = false; alert('Could not extract frame: ' + (err && err.message || err)); }); return; } loadScriptPromise('/js/jszip.min.js').then(function() { if (typeof JSZip === 'undefined') throw new Error('ZIP library failed to load'); var zip = new JSZip(); var idx = 0; var total = times.length; function next() { if (idx >= times.length) { if (status) status.textContent = 'Packaging ZIP...'; return zip.generateAsync({ type: 'blob' }).then(function(out) { add_file_output(URL.createObjectURL(out), base + '-frames.zip'); if (status) status.textContent = 'Done.'; if (btn) btn.disabled = false; }); } var t = times[idx]; if (status) status.textContent = 'Frame ' + (idx + 1) + ' of ' + total + '...'; return _efSeekTo(video, t).then(function() { ctx.drawImage(video, 0, 0, w, h); return _efCanvasBlob(canvas, format); }).then(function(blob) { var name = base + '-' + pad(idx + 1, 4) + '.' + format; return blob.arrayBuffer().then(function(ab) { zip.file(name, ab); idx++; return next(); }); }); } return next(); }).catch(function(err) { if (status) status.textContent = ''; if (btn) btn.disabled = false; alert('Could not extract frames: ' + (err && err.message || err)); }); } function _efInit() { var mode = document.getElementById('ef-mode'); if (mode) mode.addEventListener('change', function() { document.getElementById('ef-interval-row').style.display = mode.value === 'interval' ? '' : 'none'; document.getElementById('ef-timestamp-row').style.display = mode.value === 'single' ? '' : 'none'; }); var apply = document.getElementById('ef-apply'); if (apply) apply.addEventListener('click', _efApply); } document.addEventListener('DOMContentLoaded', _efInit); if (document.readyState !== 'loading') _efInit(); var _loadedScripts = {}; function loadScriptPromise(url) { if (_loadedScripts[url]) return _loadedScripts[url]; _loadedScripts[url] = new Promise(function (resolve, reject) { var s = document.createElement('script'); s.src = url; s.onload = resolve; s.onerror = reject; document.head.appendChild(s); }); return _loadedScripts[url]; } function replaceAll(find, replace, str) { return str.replace(new RegExp(find, 'g'), replace); } function beautify(str) { var result = ''; var length = str.length; var i = 0; var braceCountLeft = 0; var braceCountRight = 0; var withinQuotes = false; while (i < length) { var c = str[i]; if (c == '"' && (i == 0 || c[i - 1] != '\\')) { // non-escaped quotes withinQuotes = !withinQuotes; } if (!withinQuotes && (c == '}' || c == '{' || c == ',')) { console.log('Start####' + result); // look back and remove carriage returns and whitespace that are already there var resultIndex = result.length - 1; while (resultIndex >= 0 && (result[resultIndex] == ' ' || result[resultIndex] == '\r' || result[resultIndex] == '\n' || result[resultIndex] == '\t')) { resultIndex = resultIndex - 1; result = result.substr(0, resultIndex + 1); console.log('char ' + result[resultIndex] + '-----' + result + 'zzz ' + result.length + ' ' + resultIndex); } if (c == '{') { braceCountLeft++; result += c + '\r' + GetTabs(braceCountLeft - braceCountRight); } else if (c == '}') { braceCountRight++; // precede with carriage return result += '\r' + GetTabs(braceCountLeft - braceCountRight) + c; } else if (c == ',') { result += c + '\r' + GetTabs(braceCountLeft - braceCountRight); } var nextChar = ''; // advance through whitespace and remove carriage returns that are already there while (i < length && (str[i + 1] == ' ' || str[i + 1] == '\r' || str[i + 1] == '\n' || str[i + 1] == '\t')) { i++; } } else { result += str[i]; } i++; } return result; } function GetTabs(count) { var result = ''; for (var i = 0; i < count; i++) { result += ' '; } return result; }